package org.example.server;

import com.alipay.remoting.exception.CodecException;
import com.alipay.sofa.rpc.config.ConsumerConfig;
import com.alipay.sofa.rpc.listener.ChannelListener;
import com.alipay.sofa.rpc.transport.AbstractChannel;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.example.command.OrderCmd;
import org.example.service.FetchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class GatewayClient {

    @Value("${server.gateway.urls}")
    private String urls;
    @Value("${server.gateway.interval}")
    private int interval;
    @Value("${server.gateway.timeout}")
    private int timeout;

    @Autowired
    ThreadPoolTaskExecutor executor;
    @Autowired
    EngineServer engineServer;

    @Autowired
    Sorter sorter;

    private Map<String, FetchService> fetchServiceMap = Maps.newConcurrentMap();

    /**
     * 主动使用sofaRPC从网关读取数据
     */
    @PostConstruct
    public void init() {
        Arrays.stream(urls.split(",")).forEach((url) -> {
            System.out.println("ready to connect " + url);
            ConsumerConfig<FetchService> consumerConfig = new ConsumerConfig<>();
            consumerConfig.setInterfaceId(FetchService.class.getName())
                    .setProtocol("bolt")
                    .setTimeout(5000)
                    .setDirectUrl(url);
            consumerConfig.setOnConnect(Lists.list(new GatewayFetcherListern(consumerConfig)));
            fetchServiceMap.put(url, consumerConfig.refer());
        });
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        startFetch();
    }

    private void startFetch() {
        new Thread() {
            @Override
            public void run() {

                while (true) {
                    System.out.println("=======start fetch cmd from gateway. gateways.size = " + fetchServiceMap.size());
                    CountDownLatch countDownLatch = new CountDownLatch(fetchServiceMap.size());
                    List<OrderCmd> allCmds = new LinkedList<>();
                    fetchServiceMap.values().forEach((fetchService) -> {
                        executor.execute(() -> {
                            List<OrderCmd> fetched = fetchService.fetch();
                            if(!CollectionUtils.isEmpty(fetched)) {
                                allCmds.addAll(fetched);
                            }

                            countDownLatch.countDown();
                        });

                    });
                    try {
                        countDownLatch.await(timeout, TimeUnit.MILLISECONDS);
                        if(!CollectionUtils.isEmpty(allCmds)) {
                            List<OrderCmd> sorted = sorter.sort(allCmds);
                            System.out.println("send cmd to 撮合系统 , size="+sorted.size());
                            engineServer.send(sorted);
                        }

                    } catch (InterruptedException e) {
                        log.info("countdownlatch awaked");
                    } catch (CodecException e) {
                        log.error("countdown err", e);
                    }
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        log.info("sleep interrupted");
                    }
                }
            }
        }.start();
    }


    private class GatewayFetcherListern implements ChannelListener {

        private final ConsumerConfig<FetchService> consumerConfig;

        public GatewayFetcherListern(ConsumerConfig consumerConfig) {
            this.consumerConfig = consumerConfig;
        }

        @Override
        public void onConnected(AbstractChannel abstractChannel) {
            String url = abstractChannel.remoteAddress().toString();
            System.out.println("gateway ("+url+") connected");
            fetchServiceMap.put(url, consumerConfig.refer());
        }

        @Override
        public void onDisconnected(AbstractChannel abstractChannel) {
            String url = abstractChannel.remoteAddress().toString();
            fetchServiceMap.remove(url);
        }
    }
}
